//
// Created by song on 16-12-22.
//

#ifndef C0COMPILER_BASICBLOCK_H
#define C0COMPILER_BASICBLOCK_H


#include <set>
#include "IR.h"
#include "DefUseChain.h"
#include "FindDefinitionIRVisitor.h"

class Point{
public:
    IR* ir;
    TmpVar* var;
    Point(IR* ir, TmpVar* var):ir(ir),var(var){

    }

    bool equals(Point* p){
        return (p->ir == this->ir && p->var== this->var);
    }
};

class BasicBlock{
    vector<IR*> irList;
    set<TmpVar *> &globalVars;
    set<IRType> genIRSet={ // op that would generate new TmpVars.
            ASSIGN, DEFREF, POPPARAM, PUSHARR, READ, CALL, READCHAR,
            ADD, SUB, MUL, DIV, SL, SLE, SE, SNE, SGE, SG
    };
public:
    BasicBlock* pre, *next;
    set<BasicBlock*> from, to;
    BasicBlock(set<TmpVar *> &globalVars):globalVars(globalVars) {
        pre=NULL;
        next=NULL;
    }

    void appendIR(IR *ir) {
        irList.push_back(ir);
    }

    TmpVar* label(){
        return irList[0]->label;
    }

    string name(){
        if(label()!=NULL){
            return label()->toString();
        }else{
            if(irList.size()==1){
                return IR::irTypeStr[irList[0]->op];
            }else{
                return IR::irTypeStr[irList[0]->op] + "..." + IR::irTypeStr[lastIR()->op];
            }
        }
    }

    unsigned int id(){
        return (unsigned int) (((unsigned long) this) % 0xffff);
    }

    IR* lastIR(){
        if(!irList.empty()){
            return irList[irList.size()-1];
        }else{
            Error::nextErrorDetail << "basic block is empty";
            Error::internal(Error::Should_Not_Happen);
        }
    }

    set<IR *> *dataFlowGen(set<TmpVar*>* varGen) {

        set<IR *>* irGen = new set<IR*>();
        for(int i= (int) (irList.size() - 1); i >= 0; i--){
            IR* ir = irList[i];
            if(genIRSet.count(ir->op)>0 && ir->result!=NULL){
                if(globalVars.count(ir->result)==0) { // assign to a local variable
                    if (varGen->count(ir->result) == 0) {
                        // if in one basic block an variable is set more than once,
                        // then we only log the last one. that's why we step/for backward.
                        irGen->insert(ir);
                        varGen->insert(ir->result);
                    }
                }
            }
        }
        return irGen;
    }

    void findVarDef(set<IR*>* inSet){
        FindDefinitionIRVisitor interpreter(inSet, globalVars);
        for(int i=0; i<irList.size(); i++){
            IR* ir = irList[i];
            interpreter.visit(ir);
        }
    }

    string toString(){
        stringstream r;
        vector<IR*>::const_iterator iter = irList.begin();
        for(;iter!=irList.end(); iter++){
            r << (*iter)->print() << std::endl;
        }
        return r.str();
    }
};




#endif //C0COMPILER_BASICBLOCK_H
